from turbogears import controllers, expose, identity, widgets, validators, error_handler, validate, paginate
import routes

from gibe.plugin import add_plugin_data, plugins
from gibe.util import utcnow, utc 
from datetime import datetime, timedelta, date
from dateutil.relativedelta import relativedelta
from gibe import model

import itertools
import math
import operator
import sys
import types

from gibethemenxsy200901.relativetime import timesince

def add_utc_timezone(d):
    return d.replace(tzinfo=utc)

archives_cache = {}

def cache_decorator(cache_dict):
    def _cache_deco(_func):
        def _cache_func(*args, **kw):
            blog = kw.get('blog')

            kw_tuple = tuple(sorted(kw.items()))
            cache_now = utcnow()
            recently_modified_posts = model.Post.select(model.Post.c.blog_id == blog.blog_id, order_by=model.desc(model.Post.c.creation_time), limit = 1)
            posts_last_modified = recently_modified_posts[0].modification_time

            if kw_tuple in cache_dict:
                use_cache = True
                cache_last_modified, cache_time, payload = archives_cache[kw_tuple]
                if cache_now > cache_time + timedelta(seconds=600):
                    use_cache = False
                else:
                    if posts_last_modified > cache_last_modified:
                        use_cache = False
                if use_cache:
                    return payload

            payload = _func(*args, **kw)
            cache_dict[kw_tuple] = posts_last_modified, cache_now, payload

            return payload
        return _cache_func
    return _cache_deco

class Nxsy200901Controller(controllers.Controller):
    @cache_decorator(archives_cache)
    @expose(template="genshi:gibethemenxsy200901.templates.archives", content_type='text/html; charset=utf-8')
    @add_plugin_data(template="genshi:gibethemenxsy200901.templates.archives")
    def archives(self, blog, action, **kw):
        year = kw.get('year', None)
        if year is None:
            start_year = 1900
            end_year = 2038
        else:
            start_year = int(year)
            end_year = start_year + 1

        st = datetime(year = start_year, month = int(kw.get('month', 1)), day = int(kw.get('day', 1)))
        day = kw.get('day', None)
        month = kw.get('month', None)
        if day:
            et = st + timedelta(days=1)
        elif month:
            et = st + timedelta(days=32)
            et = datetime(et.year, month=et.month, day=1)
        elif year:
            et = st + timedelta(days=366)
            et = datetime(et.year, month=1, day=1)
        else:
            et = datetime(end_year, month=1, day=1)

        posts = model.Post.select((model.Post.c.published == True) & (model.Post.c.creation_time.between(st, et)), order_by=model.desc(model.Post.c.creation_time))

        numposts = len(posts)
        if numposts:
            firstpost = posts[-1:][0]
        else:
            firstpost = None

        now = datetime.now()
        if not year:
            then = now
        else:
            then = et

        if month:
            fpty_st = datetime(year=st.year, month=1,day=1)
            fpty_et = fpty_st + timedelta(days=366)
            fpty_et = datetime(year=fpty_et.year, month=1, day=1)
            other_posts_this_year = len(model.Post.select((model.Post.c.published == True) & (model.Post.c.creation_time.between(fpty_st, fpty_et)), order_by=model.asc(model.Post.c.creation_time)))
        else:
            other_posts_this_year = False

        if day:
            fptm_st = datetime(year=st.year, month=st.month,day=1)
            fptm_et = fptm_st + timedelta(days=32)
            fptm_et = datetime(year=fptm_et.year, month=fptm_et.month, day=1)
            other_posts_this_month = len(model.Post.select((model.Post.c.published == True) & (model.Post.c.creation_time.between(fptm_st, fptm_et)), order_by=model.asc(model.Post.c.creation_time))) > 0
        else:
            other_posts_this_month = None

        if not day:
            time_since = timesince(firstpost.creation_time, then)
            posts_per_week = "%.1f" % (float(numposts) / ((then - firstpost.creation_time).days / 7),)
        else:
            time_since = None
            posts_per_week = None

        def year_func(p):
            return date(p.creation_time.year, 1, 1)
        def month_func(p):
            return date(p.creation_time.year, p.creation_time.month, 1)
        def day_func(p):
            return date(p.creation_time.year, p.creation_time.month, p.creation_time.day)

        posts_by_year_by_month = []
        posts_by_year = sorted([(k, list(v)) for k, v in itertools.groupby(posts, year_func)], reverse= True)
        for this_year, posts_in_year in posts_by_year:
            posts_by_month = sorted([(k, list(v)) for k, v in itertools.groupby(posts_in_year, month_func)], reverse= True)
            posts_by_year_by_month.append((this_year, posts_by_month))

        return dict(posts = posts_by_year_by_month, firstpost=firstpost, numposts = len(posts), time_since=time_since, posts_per_week = posts_per_week, past = now > then, year = year, month = month, day = day, other_posts_this_year = other_posts_this_year, other_posts_this_month = other_posts_this_month)

    @expose(template="genshi:gibethemenxsy200901.templates.archives", content_type='text/html; charset=utf-8')
    @add_plugin_data(template="genshi:gibethemenxsy200901.templates.archives")
    def tag(self, blog, action, **kw):
        tagname = kw.get('tagname')

        year = kw.get('year', None)
        if year is None:
            start_year = 1900
            end_year = 2038
        else:
            start_year = int(year)
            end_year = start_year + 1

        st = datetime(year = start_year, month = int(kw.get('month', 1)), day = int(kw.get('day', 1)))
        day = kw.get('day', None)
        month = kw.get('month', None)
        if day:
            et = st + timedelta(days=1)
        elif month:
            et = st + timedelta(days=32)
            et = datetime(et.year, month=et.month, day=1)
        elif year:
            et = st + timedelta(days=366)
            et = datetime(et.year, month=1, day=1)
        else:
            et = datetime(end_year, month=1, day=1)

        posts = model.session.query(model.Post)
        posts = posts.filter(model.Post.c.published == True)
        posts = posts.filter(model.Post.c.creation_time.between(st, et))
        posts = posts.filter(posts.join_via(['tags']))
        posts = posts.filter(model.Tag.c.name == tagname) 
        posts = posts.order_by(model.desc(model.Post.c.creation_time))

        posts = list(posts)

        firstpost = posts[-1:][0]
        numposts = len(posts)

        def year_func(p):
            return date(p.creation_time.year, 1, 1)
        def month_func(p):
            return date(p.creation_time.year, p.creation_time.month, 1)
        def day_func(p):
            return date(p.creation_time.year, p.creation_time.month, p.creation_time.day)

        posts_by_year_by_month = []
        posts_by_year = sorted([(k, list(v)) for k, v in itertools.groupby(posts, year_func)], reverse= True)
        for year, posts_in_year in posts_by_year:
            posts_by_month = sorted([(k, list(v)) for k, v in itertools.groupby(posts_in_year, month_func)], reverse= True)
            posts_by_year_by_month.append((year, posts_by_month))

        return dict(posts = posts_by_year_by_month, tagname = tagname, firstpost=firstpost, numposts=numposts)

    @expose(template="genshi:gibethemenxsy200901.templates.taglist2", content_type='text/html; charset=utf-8')
    @add_plugin_data(template="genshi:gibethemenxsy200901.templates.taglist2")
    def taglist(self, blog, action, **kw):
        tags = model.session.query(model.Tag)
        now = datetime.now()

        tags_count = list(tags_by_count())

        count_of_tabs = dict(tags_count)

        pseudo_max = tags_count[7][1]

        tags_count[0] = (tags_count[0][0], pseudo_max)
        tags_count[1] = (tags_count[1][0], pseudo_max)
        tags_count[2] = (tags_count[2][0], pseudo_max)
        tags_count[3] = (tags_count[3][0], pseudo_max)
        tags_count[4] = (tags_count[4][0], pseudo_max)
        tags_count[5] = (tags_count[5][0], pseudo_max)
        tags_count[6] = (tags_count[6][0], pseudo_max)

        tag_use = list(tags_by_use())

        from pprint import pprint
        tagCloudByCount = makeCloud(28, tags_count)
        tagCloudByLastUse = makeCloud(16, [(tag, max((now - last_used).days, 15)) for tag, first_used, last_used in tag_use])
        tagCloudByFirstUse = makeCloud(16, [(tag, max((now - first_used).days, 15)) for tag, first_used, last_used in tag_use])

        tags_alphabetical = sorted([(tag.name, count_of_tabs[tag.tag_id], tagCloudByCount[tag.tag_id], tagCloudByFirstUse[tag.tag_id], tagCloudByLastUse[tag.tag_id]) for tag, num in blog.all_tags_count(min=1)])
        pprint(tags_alphabetical)

        return dict(tags_alphabetical = tags_alphabetical)

def makeCloud(steps, input):
    if not type(input) == types.ListType or len(input) <= 0 or steps <= 0:
        raise InvalidInputException,\
              "Please be sure steps > 0 and your input list is not empty."
    else:
        temp, newThresholds, results = [], [], {}
        for item in input:
            if not type(item) == types.TupleType:
                raise InvalidInputException, "Be sure input list holds tuples."
            else: temp.append(item[1])
        maxWeight = float(max(temp))
        minWeight = float(min(temp))
        newDelta = (maxWeight - minWeight)/float(steps)
        for i in range(steps + 1):
           newThresholds.append((100 * math.log((minWeight + i * newDelta) + 2), i))
        for tag in input:
            fontSet = False
            for threshold in newThresholds[1:int(steps)+1]:
                if (100 * math.log(tag[1] + 2)) <= threshold[0] and not fontSet:
                    results[tag[0]] = str(threshold[1])
                    fontSet = True
        return results

def tags_by_count():
    s = model.select([model.post_tag.c.tag_id, model.func.count(model.post_tag.c.post_id)], group_by=[model.post_tag.c.tag_id], order_by=[model.desc(model.func.count(model.post_tag.c.post_id))])
    r = s.execute()
    for tag_id, num in r:
        yield tag_id, num 

def tags_by_use():
    s = model.select([model.post_tag.c.tag_id, model.func.min(model.Post.c.creation_time), model.func.max(model.Post.c.creation_time)], model.post_tag.c.post_id == model.Post.c.post_id, group_by=[model.post_tag.c.tag_id])
    r = s.execute()
    for tag_id, first_used, last_used in r:
        yield tag_id, first_used, last_used

def tags_by_most_used_recently(blog, limit):
    recent_posts = blog.recent_posts(num = 100)
    ids = [post.post_id for post in recent_posts]
    s = model.select([model.post_tag.c.tag_id, model.func.count(model.post_tag.c.post_id)], model.post_tag.c.post_id.in_(*ids), group_by=[model.post_tag.c.tag_id], order_by=[model.desc(model.func.count(model.post_tag.c.post_id))])
    r = s.execute()
    for tag_id, num in r:
        if num < limit:
            continue
        yield model.Tag.get(tag_id), num
